package db_migrater

import (
	"fmt"
	"goravel/app/models"
	"strconv"
	"strings"
	"time"

	"slices"

	"github.com/goravel/framework/contracts/console"
	"github.com/goravel/framework/contracts/console/command"
	"github.com/goravel/framework/facades"
	"gorm.io/gorm"

	oracle "github.com/seelly/gorm-oracle"
)
 
var actions = []string{
    "create",
    "all",
    "get",
    "edit",
    "destory",
}

type SourceDatabase struct {
}

// Signature The name and signature of the console command.
func (receiver *SourceDatabase) Signature() string {
    return "db-migrate:source_database"
}

// Description The console command description.
func (receiver *SourceDatabase) Description() string {
    return "数据库迁移，原数据库管理，注意：选项要在参数前定义"
}

// Extend The console command extend.
func (receiver *SourceDatabase) Extend() command.Extend {
    return command.Extend{
        Category: "db-migrater", // 设置分类
        Flags: []command.Flag{
            &command.StringFlag{
                Name: "migration_task_id",
                Value: "",
                Usage: "Migration task ID",
            },
			&command.StringFlag{
				Name: "type",
				Aliases: []string{"t"},
				Value: "",
				Usage: "Optional database types, supporting: Oracle",
			},
			&command.StringFlag{
				Name: "ip",
				Aliases: []string{"i"},
				Value: "",
				Usage: "Database IP",
			},
			&command.StringFlag{
				Name: "port",
				Aliases: []string{"p"},
				Value: "",
				Usage: "Database port",
			},
			&command.StringFlag{
				Name: "username",
                Aliases: []string{"U"},
                Value: "",
                Usage: "Database access username",
			},
            &command.StringFlag{
                Name: "password",
                Aliases: []string{"P"},
                Value: "",
                Usage: "Database access password",
            },
            &command.StringFlag{
                Name: "schema",
                Aliases: []string{"s"},
                Value: "",
                Usage: "Schema name",
            },
            &command.StringFlag{
                Name: "service",
                Value: "",
                Usage: "Oracle service name",
            },
        },
    }
}

// Handle Execute the console command.
func (receiver *SourceDatabase) Handle(ctx console.Context) error {
    action := ctx.Argument(0);

    if ! slices.Contains(actions, action) {
        ctx.Warning("不支持的操作【" + action + "】，允许操作：" + strings.Join(actions, "、"))
        return nil
    }

    if action == "create" {
        receiver.actionCreate(ctx)
    } else if action == "all" {
        receiver.actionAll(ctx)
    }
    // todo ↑↑↑ 修改成动态调用方法 ↑↑↑
    // method := reflect.ValueOf(receiver).MethodByName(action)
    // method.Call(ctx)

    return nil
}

func (receiver *SourceDatabase) actionCreate(ctx console.Context) error {
    /** 
    example: 
    ./artisan db-migrate:source_database 
        --migration_task_id={$migration_task_id} 
        --type=Oracle 
        --ip=0.0.0.0 
        --port=12345 
        --username=root 
        --password=rootpass 
        --schema=root 
        [--service=ORCL]
        create
    */
    ctx.Info("开始创建原数据库")

    IsTest := "no"
    for {
        input, _ := ctx.Ask("是否测试数据库连接？yes/no")
        if input == "yes" || input == "no" {
            IsTest = input
            break
        }
    }

    migrationTaskID, _ := strconv.Atoi(ctx.Option("migration_task_id"))

    // model    
    sourceDatabase := models.SourceDatabase{
        MigrationTaskID : uint(migrationTaskID),
        Type            : ctx.Option("type"),
        IP              : ctx.Option("ip"),
        Port            : ctx.Option("port"),
        Username        : ctx.Option("username"),
        Password        : ctx.Option("password"),
        Schema          : ctx.Option("schema"),
        Service         : ctx.Option("service"),
    }
    // todo ↑↑↑ need validate ↑↑↑

    if IsTest == "yes" {
        ctx.Info("开始测试数据库连接")

        // todo 需要测试连接
        var TestResult bool = false
        var TestError error 
        ctx.Spinner("Loading...", console.SpinnerOption{
            Action: func() error {
                // when to stop the spinner
                time.Sleep(1 * time.Second)
                
                portInt, _ := strconv.Atoi(sourceDatabase.Port)
                databaseUrl := oracle.BuildUrl(
                    sourceDatabase.IP, 
                    portInt, 
                    sourceDatabase.Service, 
                    sourceDatabase.Username, 
                    sourceDatabase.Password, 
                    nil,
                )
                ctx.Info(databaseUrl)
                _, err := gorm.Open(oracle.Open(databaseUrl), &gorm.Config{})
                if err != nil {
                    TestError = err
                    return nil
                }

                TestResult = true

                // todo 有其他oracle链接库，需要选择
                //      https://www.yisu.com/ask/63641572.html
                //      https://blog.csdn.net/homonym/article/details/142522208
                //      https://www.jb51.net/article/251496.htm
                // todo 需要抽离公共代码，添加单独测试功能

                return nil
            },
        })

        if ! TestResult {
            ctx.Error("数据库连接失败, Error：" + TestError.Error())
            return nil
        }
        ctx.Success("数据库连接成功")
    } else {
        ctx.Info("不测试数据库连接")
    }
    
    err := facades.Orm().Query().Create(&sourceDatabase)
    if err != nil {
        ctx.Error("创建失败：" + err.Error())
        return nil
    }

    ctx.Success("原数据库创建成功，ID：" + strconv.Itoa(int(sourceDatabase.ID)))
    return nil
}

func (receiver *SourceDatabase) actionAll(ctx console.Context) error {
    ctx.Info("查询全部原数据库信息")

    var sourceDatabases []models.SourceDatabase
    facades.Orm().Query().Find(&sourceDatabases)

    separator := "***************"
    for i, sourceDatabase := range sourceDatabases {
        ctx.Line(separator + " row " + strconv.Itoa(i + 1) + ". " + separator)

        // sourceDatabaseType := reflect.TypeOf(sourceDatabase)
        // sourceDatabaseValue := reflect.ValueOf(sourceDatabase)
        // for i := range sourceDatabaseType.NumField() {
        //     sourceDatabaseValue.Field(i).Interface()
        //     ctx.Line(fmt.Sprintf("%20s", sourceDatabaseType.Field(i).Name) + " : ")
        // }
        // todo ↑↑↑ 这里需要修改自动遍历字段，使用reflect方式会额外遍历Model、Timestamps。

        ctx.Line(fmt.Sprintf("%20s", "id : ") + strconv.Itoa(int(sourceDatabase.ID)))
        ctx.Line(fmt.Sprintf("%20s", "type : ") + sourceDatabase.Type)
        ctx.Line(fmt.Sprintf("%20s", "ip : ") + sourceDatabase.IP)
        ctx.Line(fmt.Sprintf("%20s", "port : ") + sourceDatabase.Port)
        ctx.Line(fmt.Sprintf("%20s", "username : ") + sourceDatabase.Username)
        ctx.Line(fmt.Sprintf("%20s", "password : ") + sourceDatabase.Password)
        ctx.Line(fmt.Sprintf("%20s", "schema : ") + sourceDatabase.Schema)
        ctx.Line(fmt.Sprintf("%20s", "service : ") + sourceDatabase.Service)
        ctx.Line(fmt.Sprintf("%20s", "created_at : ") + sourceDatabase.CreatedAt.Format("2006-01-02 15:04:05"))
        ctx.Line(fmt.Sprintf("%20s", "updated_at : ") + sourceDatabase.UpdatedAt.Format("2006-01-02 15:04:05"))
        // todo ↑↑↑ timestamp.Format格式化错误 ↑↑↑
    }

    ctx.NewLine(1)
    ctx.Line("Total lines: " + strconv.Itoa(len(sourceDatabases)))

    // ctx.TwoColumnDetail("Total Size", fmt.Sprintf("%.3f MB", 110))

    return nil
}

func (receiver *SourceDatabase) actionGet(ctx console.Context) error {
    ctx.Warning("敬请期待")
    return nil
}

func (receiver *SourceDatabase) actionEdit(ctx console.Context) error {
    ctx.Warning("敬请期待")
    return nil
}

func (receiver *SourceDatabase) actionDestory(ctx console.Context) error {
    ctx.Warning("暂时不允许删除")
    return nil
}